#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "scache.h"
#include "mylog.h"

/* |page_list      |sloti
 *  ---------------^-----------
 * |      |      |      |      |
 *  -------------^-------------
 *               |pagei
 */ 


static int grow(scache * p)
{
	assert(p != NULL);
	if(p->slabi < VLA_SIZEOF(p->slab_list)){
		return 0;
	}
	void ** sp = vla_set(p->slab_list, VLA_SIZEOF(p->slab_list));
	if(sp == NULL){
		return -1;
	}
	*sp = malloc(p->slot_size * p->slab_size);
	if(*sp == NULL){
		return -2;
	}
#ifndef NDEBUG
	logit(LV_DEBUG, "scache new %d[%p] setto [%p]", p->slot_size * p->slab_size, *sp, sp);
#endif
	return 0;
}

scache * scache_create(unsigned int slot_size)
{
	if(slot_size == 0 || slot_size > SLOT_SIZE_MAX){
		return NULL;
	}
#ifndef NDEBUG
	logit(LV_DEBUG, "slab create slot_size: %u", slot_size);
#endif
	scache * sc = (scache *)malloc(sizeof(struct scache));
	if(sc == NULL){
		return NULL;
	}
	sc->slot_size = slot_size;
	sc->slab_size = SLAB_SIZE_DEF;
	sc->sloti = sc->slabi = 0;
	sc->slab_list = vla_new(sizeof(void *));
	if(grow(sc) < 0){
		vla_free(sc->slab_list);
		free(sc);
		return NULL;
	}
	return sc;
}

void * scache_alloc(scache *p)
{
	if(p == NULL){
		return NULL;
	}
	if(p->sloti == p->slab_size){
		p->sloti = 0;
		p->slabi ++;
	}
	if(p->slabi == VLA_SIZEOF(p->slab_list) && grow(p) < 0){
		return NULL;
	}
	void ** sp = vla_get(p->slab_list, p->slabi);
	assert(sp != NULL && *sp != NULL);
#ifndef NDEBUG
	logit(LV_DEBUG, "scache alloc %d[%p -> %p]", p->slot_size, *sp, *sp + p->slot_size * p->sloti);
#endif
	return *sp + p->slot_size * (p->sloti ++);
}

//recall all mem, all object is unavailable
int scache_clear(scache *p)
{
	if(p == NULL){
		return -1;
	}
#ifndef NDEBUG
	logit(LV_DEBUG, "scache clear %u", p->slot_size);
#endif
	p->slabi = 0;
	p->sloti = 0;
	return 0;
}

//can't free one slot
int scache_free(scache *p, void * s)
{
	return -1;
}

int scache_destroy(scache * p)
{
	if(p == NULL){
		return -1;
	}
	void ** sp = NULL;
	VLA_FOR_EACH(sp, p->slab_list){
#ifndef NDEBUG
		logit(LV_DEBUG, "scache free: [%p] from [%p]", *sp, sp);
#endif
		free(*sp);
	}
	free(p->slab_list);
	p->slab_list = NULL;
#ifndef NDEBUG
	logit(LV_DEBUG, "scache destroy %u", p->slot_size);
#endif
	p->slabi = p->sloti = p->slot_size = p->slab_size = 0;
	free(p);
	return 0;
}

#ifdef SCACHE_MAIN
int main(int argc, char ** argv)
{
	logon();
	scache *p = scache_create(sizeof(struct scache));
	scache *s = NULL;
	int i = 0;
	for(i=0; i<50; i++){
		s = scache_alloc(p);
		printf("alloc %d[%p] \n", sizeof(struct scache), s);
	}
	scache_destroy(p);
	logoff();
	return 0;
}
#endif
